﻿#include "PinErrListener.h"
#include "global.h"

extern int ExceptStep;

PinErrListener::PinErrListener()
{

}

int exCeptTimes = 0;

void outputHelpInfo(int id, int line, QString &statement)
{
    QString errstr;

    switch (id) {
    case 1:
    {
#if defined(Q_OS_MAC)
#ifdef NDD_PIN_FUN
        errstr = QString(u8"错误：行号 %1 附近语句 %2 丢失冒号';'语句结束符号").arg(line).arg(statement);
#else
        errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 丢失冒号';'语句结束符号\033[0m").arg(line).arg(statement);
#endif
#else
        errstr = QString(u8"错误：行号 %1 附近语句 %2 丢失冒号';'语句结束符号").arg(line).arg(statement);
#endif
    }
        break;
    case 2:
    {
#if defined(Q_OS_MAC)
#ifdef NDD_PIN_FUN
        errstr = QString(u8"错误：行号 %1 附近语句 %2 存在中文‘；’冒号，非法字符，请修改为英文冒号").arg(line).arg(statement);
#else
        errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘；’冒号，非法字符，请修改为英文冒号\033[0m").arg(line).arg(statement);
#endif
#else
        errstr = QString(u8"错误：行号 %1 附近语句 %2 存在中文‘；’冒号，非法字符，请修改为英文冒号").arg(line).arg(statement);
#endif
    }
        break;
    case 3:
    {
#if defined(Q_OS_MAC)
#ifdef NDD_PIN_FUN
        errstr = QString(u8"错误：行号 %1 附近语句 %2 存在中文‘，’逗号，非法字符，请修改为英文逗号").arg(line).arg(statement);
#else
        errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘，’逗号，非法字符，请修改为英文逗号\033[0m").arg(line).arg(statement);
#endif
#else
        errstr = QString(u8"错误：行号 %1 附近语句 %2 存在中文‘，’逗号，非法字符，请修改为英文逗号").arg(line).arg(statement);
#endif
    }
        break;
    case 4:
    {
#if defined(Q_OS_MAC)
#ifdef NDD_PIN_FUN
        errstr = QString(u8"错误：行号 %1 附近语句 %2 存在中文‘（’ 或 ‘）’ 括号，非法字符，请修改为英文括号").arg(line).arg(statement);
#else
        errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘（’ 或 ‘）’ 括号，非法字符，请修改为英文括号\033[0m").arg(line).arg(statement);
#endif
#else      
        errstr = QString(u8"错误：行号 %1 附近语句 %2 存在中文‘（’ 或 ‘）’ 括号，非法字符，请修改为英文括号").arg(line).arg(statement);
#endif
    }
        break;
    default:
        break;
    }

    std::cerr << errstr.toStdString() << std::endl;
}

void checkMoreErrorTips(int line, const std::string &msg)
{
    //检查是不是丢失了冒号。
    QString errStr = QString::fromStdString(msg);

    QChar vaildCharLeft(65288);//中文左括号
    QChar vaildCharRight(65289);//中文右括号

    QChar vaildChar(65307);//中文冒号。
    QChar vaildChar1(65292);//中文逗号

    if(errStr.startsWith(u8"输入字段没有合法的可选语法去匹配") || errStr.startsWith("no viable alternative at input"))
    {
        int start = errStr.indexOf('\'');
        int end = errStr.lastIndexOf('\'');

        QString statement ;

        if(start != -1 && end > start)
        {
            statement = errStr.mid(start+1,end-start-1);
            statement = statement.trimmed();
        }



        //可能是缺少了；号，语句没有结束
        if(statement.contains("\\n"))
        {
            outputHelpInfo(1,line,statement);
        }
        else if (statement.contains(vaildCharLeft))
        {
            outputHelpInfo(4, line, statement);
        }
        else if (statement.contains(vaildCharRight))
        {
            outputHelpInfo(4, line, statement);
        }

        if(statement.endsWith(vaildChar))
        {
            outputHelpInfo(2,line,statement);
        }
        else if(statement.endsWith(vaildChar1))
        {
            outputHelpInfo(3,line,statement);
            //QString errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘，’逗号，非法字符，请修改为英文逗号\033[0m").arg(line).arg(statement);

            //std::cerr << errstr.toStdString() << std::endl;
        }
    }
    else if(errStr.startsWith(u8"无法匹配的输入字段") || errStr.startsWith(u8"mismatched input"))
    {
        int start = errStr.indexOf('\'');
        int end = errStr.indexOf('\'',start+1);

        QString statement ;

        if(start != -1 && end > start)
        {
            statement = errStr.mid(start+1,end-start-1);
            statement = statement.trimmed();
        }

        if(statement.contains(vaildCharLeft) || statement.contains(vaildCharRight))
        {
            //QString errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘（’ 或 ‘）’ 括号，非法字符，请修改为英文括号\033[0m").arg(line).arg(statement);
            outputHelpInfo(4,line,statement);
            //std::cerr << errstr.toStdString() << std::endl;
        }
        else if(statement.contains(vaildChar))
        {
            //QString errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘；’冒号，非法字符，请修改为英文冒号\033[0m").arg(line).arg(statement);
            outputHelpInfo(2,line,statement);
            //std::cerr << errstr.toStdString() << std::endl;
        }
        else if(statement.contains(vaildChar1))
        {
            //QString errstr = QString(u8"\033[31m错误：行号 %1 附近语句 %2 存在中文‘，’逗号，非法字符，请修改为英文逗号\033[0m").arg(line).arg(statement);
            outputHelpInfo(3,line,statement);
            //std::cerr << errstr.toStdString() << std::endl;
        }
    }


}

void PinErrListener::syntaxError(
    antlr4::Recognizer *recognizer,
    antlr4::Token *offendingSymbol,
    size_t line,
    size_t charPositionInLine,
    const std::string &msg,
    std::exception_ptr e)
{
    //还没有进入监听器就失败了，说明语法文件存在明显不可识别的错误，比如中文字符，乱码等。
    if (ExceptStep == 1 && pSrcCode != nullptr)
    {
        //从源码获取错误的地方，然后报错即可。
        QList<QByteArray> byteList = pSrcCode->split('\n');

        //下面两句的效果，和pSrcCode、tokens效果是一样的。
        //antlr4::CommonTokenStream* tokens1 = dynamic_cast<antlr4::CommonTokenStream*>(recognizer->getInputStream());
        //std::string content = tokens1->getTokenSource()->getInputStream()->toString();

        QString errmsg;

        if (line >= 1 && line < byteList.size())
        {
            QByteArray prevlineBytes;
            if(line >= 2)
            {
                prevlineBytes = byteList.at(line - 2);
            }

            QByteArray lineBytes = byteList.at(line - 1);

#if defined(Q_OS_MAC)
#ifdef NDD_PIN_FUN
            errmsg = QString(u8"错误：行号 %1 错误语句 %2 \n%3\n").arg(line).arg(QString(lineBytes)).arg(QStr(msg));
#else
            errmsg = QString(u8"\033[31m错误：行号 %1 错误语句 %2 \n%3\n\033[0m").arg(line).arg(QString(lineBytes)).arg(QStr(msg));
#endif
#else
            errmsg = QString(u8"错误：行号 %1 错误语句 %2 \n%3\n").arg(line).arg(QString(lineBytes)).arg(QStr(msg));
#endif


            //\t会导致缩进不对，替换为空格,因为\t的显示宽度是不确定的。
            for(int i = 0,c=lineBytes.size(); i < c; ++i)
            {
                if(lineBytes.at(i) == '\t')
                {
                    lineBytes[i] = ' ';
                }
            }

            std::cout << prevlineBytes.toStdString();// 已经有换行了。 << std::endl;
            std::cout << lineBytes.toStdString();// << std::endl;

            QByteArray indexBytes(charPositionInLine + 1, ' ');
            for (int i = charPositionInLine, c = 0; i >= 0 && c < 4; --i, ++c)
            {
                indexBytes[i] = '^';
            }
            std::cout << indexBytes.toStdString() << std::endl;
            
        }
        else
        {
            errmsg = QString(u8"错误：行号 %1 %2\n").arg(line).arg(QStr(msg));

        }
        std::cerr << errmsg.toStdString() << std::endl;

        checkMoreErrorTips(line,msg);

        QString err = QString::fromStdString(msg);
        AbortException(err);

        //std::exit(1);
        return;
    }
    //已经发现，tokens->size()里面没有包含隐藏的空白通道的字符。
    // 所以要获取完整的语句，必须要先调用tokens->fill()，大坑

    QString errmsg = QString(u8"错误：行号 %1 %2").arg(line+getLastFunStartLineNum()).arg(QStr(msg));

    exCeptTimes++;

    //发现tokens->fill() 可能再次触发异常，进而异常多次进入。
    if(exCeptTimes >= 2)
    {
        //加个保护，如果是异常循环除非，最多2次就退出，避免栈溢出死循环。
    }
    else
    {
    std::cerr << errmsg.toStdString() << std::endl;

    // 获取出错令牌的索引
    int startIndex = -1;

    if(offendingSymbol != nullptr) //20250221 发现居然可能为nullptr，导致崩溃
    {
        startIndex = offendingSymbol->getTokenIndex();
    }
    int stopIndex = startIndex;

    std::string errorLineText;

    if(startIndex != -1 && startIndex <= stopIndex && tokens != nullptr )
    {
        tokens->fill();

        while (startIndex >=0 && startIndex < tokens->size() && (tokens->get(startIndex)->getLine() == line)) {
            --startIndex;
        }
        ++startIndex;

        while (stopIndex < tokens->size() && (tokens->get(stopIndex)->getLine() < (line+1))) {
            ++stopIndex;
        }
        stopIndex--;

        if (startIndex >= 0 && startIndex <= stopIndex)
        {
            // 获取出错行的文本
            errorLineText = tokens->getText(antlr4::misc::Interval((size_t)startIndex, (size_t)stopIndex));
        }

    }
    else
    {
        FUN_SYM_INFO* pFun = nullptr;

        //获取当前函数对应错误行的代码。
        if (getPinRunStatus() == INTO_GLOBAL)
        {
            pFun = findFunBlock("quanju_dingyi");

            if(pFun == nullptr)
            {
                pFun = findFunBlock("global");
        }
        }
        else
        {
            pFun = findFunBlock(getCurFunName());
        }

        if (pFun != nullptr)
        {

            QString lineContents = QString::fromStdString(*pFun->funBlock);
            QStringList lineList = lineContents.split("\n");
            if (line >= 1 && line < lineList.size())
            {
                errorLineText = lineList.at(line - 1).toStdString();
            }
        }
    }

    //如果还在全局区发生错误
    int offsetline = 0;

    if(getPinRunStatus() == INTO_GLOBAL)
    {
        FUN_SYM_INFO* pGlobal = findFunBlock("quanju_dingyi");
        if(pGlobal != nullptr)
        {
            offsetline = pGlobal->funNameLineNum - 1;//因为全局是个特例，其第1行是从quanju_dingyi本身开始的。
        }
        else
        {
            pGlobal = findFunBlock("global");
            if(pGlobal != nullptr)
            {
                offsetline = pGlobal->funNameLineNum - 1;//因为全局是个特例，其第1行是从quanju_dingyi本身开始的。
            }
        }
    }
    else
    {
         offsetline = getLastFunStartLineNum();
    }
    QString errMsg;

    if(errorLineText != "")
    {
        errMsg = QString(u8"语法错误在第 %1 行 %2列 字段 \"%3\"\n%4 \n错误语句是%5").arg(offsetline + line).arg(charPositionInLine).arg(QStr((offendingSymbol!=nullptr)?offendingSymbol->getText():"")).arg(QStr(msg)).arg(QStr(errorLineText));
    }
    else if(tokens != nullptr)
    {
        tokens->fill();

        QString contens = QString::fromStdString(tokens->getText());

        QStringList lineList = contens.split('\n');

        if(offsetline + line -1 < lineList.size())
        {
            QString errline = lineList.at(offsetline + line -1);
            errMsg = QString(u8"语法错误在第 %1 行 %2列 字段 \"%3\"\n%4 \n错误语句是%5").arg(offsetline + line).arg(charPositionInLine).arg(QStr((offendingSymbol!=nullptr)?offendingSymbol->getText():"")).arg(QStr(msg)).arg(errline);

        }
    }
    else
    {
        errMsg = QString(u8"语法错误在第 %1 行 %2列 字段 \"%3\"\n%4").arg(offsetline + line).arg(charPositionInLine).arg(QStr((offendingSymbol!=nullptr)?offendingSymbol->getText():"")).arg(QStr(msg));

    }

    std::cerr << errMsg.toStdString() << std::endl << std::endl;
    }

   

    printStackInfo();

    //std::exit(1);//是在插件中，不能退出。
    //QString ex(e.what());
    //AbortException(ex);
    std::rethrow_exception(e);
    /*if (e) {
        try {
            std::rethrow_exception(e);

        } catch (const std::exception& ex) {
            std::cerr << "Exception: " << ex.what() << std::endl;
        }
    }*/
}


